//      This code came from: http://www.dotnetthis.com/Articles/Crypto.htm
//
//    This sample code is provided "AS IS" with no warranties, and confers no rights.
//
//    ATTENTION: This sample is designed to be more of a tutorial rather than something
//                you can copy and paste in the production code!
//                See the topic on "making it industry strength" in the blog.
//
using System;
using System.IO;
using System.Security.Cryptography;

//
// Sample encrypt/decrypt functions
//    Parameter checks and error handling are ommited for better readability
//
namespace Lizk.Utilities
{
    /// <summary>
    /// Offers simple encryption using Rijndael algorithm. This class was aquired from  http://www.dotnetthis.com/Articles/Crypto.htm. As stated on the website, this class is not of production quality. But useful for simple string encryption.
    /// </summary>
    public class Encryption
    {
        /// <summary>
        /// Encrypts an array of bytes using a Key and an Initialization Vector
        /// </summary>
        /// <param name="clearData">The data to be encrypted</param>
        /// <param name="Key">The key to encrypt it with</param>
        /// <param name="IV">The Initialization Vector</param>
        /// <returns>An encrypted byte array</returns>
        private static byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream that is going to accept the encrypted bytes
            MemoryStream ms = new MemoryStream();

            // Create a symmetric algorithm.
            // We are going to use Rijndael because it is strong and available on all platforms.
            // You can use other algorithms, to do so substitute the next line with something like
            //                      TripleDES alg = TripleDES.Create();
            Rijndael alg = Rijndael.Create();

            // Now set the key and the IV.
            // We need the IV (Initialization Vector) because the algorithm is operating in its default
            // mode called CBC (Cipher Block Chaining). The IV is XORed with the first block (8 byte)
            // of the data before it is encrypted, and then each encrypted block is XORed with the
            // following block of plaintext. This is done to make encryption more secure.
            // There is also a mode called ECB which does not need an IV, but it is much less secure.
            alg.Key = Key;
            alg.IV = IV;

            // Create a CryptoStream through which we are going to be pumping our data.
            // CryptoStreamMode.Write means that we are going to be writing data to the stream
            // and the output will be written in the MemoryStream we have provided.
            CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);

            // Write the data and make it do the encryption
            cs.Write(clearData, 0, clearData.Length);

            // Close the crypto stream (or do FlushFinalBlock).
            // This will tell it that we have done our encryption and there is no more data coming in,
            // and it is now a good time to apply the padding and finalize the encryption process.
            cs.Close();

            // Now get the encrypted data from the MemoryStream.
            // Some people make a mistake of using GetBuffer() here, which is not the right way.
            byte[] encryptedData = ms.ToArray();
            return encryptedData;

        }

        /// <summary>
        /// Decrypts an array of data that has been encrypted using the Encrypt command.
        /// </summary>
        /// <param name="cipherData">The data to decrypt</param>
        /// <param name="Key">The Key to decrypt it with</param>
        /// <param name="IV">An Initialization Vector</param>
        /// <returns>A decrypted data byte array</returns>
        private static byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV)
        {

            // Create a MemoryStream that is going to accept the decrypted bytes

            MemoryStream ms = new MemoryStream();



            // Create a symmetric algorithm.

            // We are going to use Rijndael because it is strong and available on all platforms.

            // You can use other algorithms, to do so substitute the next line with something like

            //                      TripleDES alg = TripleDES.Create();

            Rijndael alg = Rijndael.Create();



            // Now set the key and the IV.

            // We need the IV (Initialization Vector) because the algorithm is operating in its default

            // mode called CBC (Cipher Block Chaining). The IV is XORed with the first block (8 byte)

            // of the data after it is decrypted, and then each decrypted block is XORed with the previous

            // cipher block. This is done to make encryption more secure.

            // There is also a mode called ECB which does not need an IV, but it is much less secure.

            alg.Key = Key;

            alg.IV = IV;



            // Create a CryptoStream through which we are going to be pumping our data.

            // CryptoStreamMode.Write means that we are going to be writing data to the stream

            // and the output will be written in the MemoryStream we have provided.

            CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);



            // Write the data and make it do the decryption

            cs.Write(cipherData, 0, cipherData.Length);



            // Close the crypto stream (or do FlushFinalBlock).

            // This will tell it that we have done our decryption and there is no more data coming in,

            // and it is now a good time to remove the padding and finalize the decryption process.

            cs.Close();



            // Now get the decrypted data from the MemoryStream.

            // Some people make a mistake of using GetBuffer() here, which is not the right way.

            byte[] decryptedData = ms.ToArray();



            return decryptedData;

        }

        /// <summary>
        /// Encrypts a string using a password
        /// </summary>
        /// <param name="clearText">The text to be encrypted</param>
        /// <param name="Password">The encryption password</param>
        /// <returns>An encrypted string</returns>
        public static string Encrypt(string clearText, string Password)
        {
            // First we need to turn the input string into a byte array.
            byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);

            // Then, we need to turn the password into Key and IV
            // We are using salt to make it harder to guess our key using a dictionary attack -
            // trying to guess a password by enumerating all possible words.
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                        new byte[] { 0x85, 0x7d, 0xa1, 0x6c, 0x2a, 0x4f, 0x23, 0x78, 0x12, 0xf2, 0xa3, 0xe2, 0xaa });

            // Now get the key/IV and do the encryption using the function that accepts byte arrays.
            // Using PasswordDeriveBytes object we are first getting 32 bytes for the Key
            // (the default Rijndael key length is 256bit = 32bytes) and then 16 bytes for the IV.
            // IV should always be the block size, which is by default 16 bytes (128 bit) for Rijndael.
            // If you are using DES/TripleDES/RC2 the block size is 8 bytes and so should be the IV size.
            // You can also read KeySize/BlockSize properties off the algorithm to find out the sizes.
            byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string.
            // A common mistake would be to use an Encoding class for that. It does not work
            // because not all byte values can be represented by characters.
            // We are going to be using Base64 encoding that is designed exactly for what we are
            // trying to do.
            return Convert.ToBase64String(encryptedData);
        }
        
        /// <summary>
        /// Encrypts a byte array using a string password
        /// </summary>
        /// <param name="clearData">The data to be encrypted</param>
        /// <param name="Password">The password to encrypt the data with</param>
        /// <returns>An encrypted byte array</returns>
        public static byte[] Encrypt(byte[] clearData, string Password)
        {
            // We need to turn the password into Key and IV.

            // We are using salt to make it harder to guess our key using a dictionary attack -

            // trying to guess a password by enumerating all possible words.

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,

                        new byte[] { 0x85, 0x7d, 0xa1, 0x6c, 0x2a, 0x4f, 0x23, 0x78, 0x12, 0xf2, 0xa3, 0xe2, 0xaa });



            // Now get the key/IV and do the encryption using the function that accepts byte arrays.

            // Using PasswordDeriveBytes object we are first getting 32 bytes for the Key

            // (the default Rijndael key length is 256bit = 32bytes) and then 16 bytes for the IV.

            // IV should always be the block size, which is by default 16 bytes (128 bit) for Rijndael.

            // If you are using DES/TripleDES/RC2 the block size is 8 bytes and so should be the IV size.

            // You can also read KeySize/BlockSize properties off the algorithm to find out the sizes.

            return Encrypt(clearData, pdb.GetBytes(32), pdb.GetBytes(16));



        }

        /// <summary>
        /// Encrypt a file using a password.
        /// </summary>
        /// <param name="fileIn">The file to encrypt</param>
        /// <param name="fileOut">Path to where the encrypted file will be saved</param>
        /// <param name="Password">The password to encrypt the file with</param>
        public static void Encrypt(string fileIn, string fileOut, string Password)
        {

            // First we are going to open the file streams

            FileStream fsIn = new FileStream(fileIn, FileMode.Open, FileAccess.Read);

            FileStream fsOut = new FileStream(fileOut, FileMode.OpenOrCreate, FileAccess.Write);



            // Then we are going to derive a Key and an IV from the Password and create an algorithm

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,

                        new byte[] { 0x85, 0x7d, 0xa1, 0x6c, 0x2a, 0x4f, 0x23, 0x78, 0x12, 0xf2, 0xa3, 0xe2, 0xaa });



            Rijndael alg = Rijndael.Create();



            alg.Key = pdb.GetBytes(32);

            alg.IV = pdb.GetBytes(16);



            // Now create a crypto stream through which we are going to be pumping data.

            // Our fileOut is going to be receiving the encrypted bytes.

            CryptoStream cs = new CryptoStream(fsOut, alg.CreateEncryptor(), CryptoStreamMode.Write);



            // Now will will initialize a buffer and will be processing the input file in chunks.

            // This is done to avoid reading the whole file (which can be huge) into memory.

            int bufferLen = 4096;

            byte[] buffer = new byte[bufferLen];

            int bytesRead;



            do
            {

                // read a chunk of data from the input file

                bytesRead = fsIn.Read(buffer, 0, bufferLen);



                // encrypt it

                cs.Write(buffer, 0, bytesRead);



            } while (bytesRead != 0);



            // close everything

            cs.Close(); // this will also close the unrelying fsOut stream

            fsIn.Close();

        }
        
        /// <summary>
        /// Decrypts a string that has been encrypted using Encrypt function
        /// </summary>
        /// <param name="cipherText">The string to decrypt</param>
        /// <param name="Password">The password to decrypt the string with</param>
        /// <returns>A decrypted string or jibberish if the password was wrong</returns>
        public static string Decrypt(string cipherText, string Password)
        {

            // First we need to turn the input string into a byte array.

            // We presume that Base64 encoding was used

            byte[] cipherBytes = Convert.FromBase64String(cipherText);



            // Then, we need to turn the password into Key and IV

            // We are using salt to make it harder to guess our key using a dictionary attack -

            // trying to guess a password by enumerating all possible words.

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,

                        new byte[] { 0x85, 0x7d, 0xa1, 0x6c, 0x2a, 0x4f, 0x23, 0x78, 0x12, 0xf2, 0xa3, 0xe2, 0xaa });



            // Now get the key/IV and do the decryption using the function that accepts byte arrays.

            // Using PasswordDeriveBytes object we are first getting 32 bytes for the Key

            // (the default Rijndael key length is 256bit = 32bytes) and then 16 bytes for the IV.

            // IV should always be the block size, which is by default 16 bytes (128 bit) for Rijndael.

            // If you are using DES/TripleDES/RC2 the block size is 8 bytes and so should be the IV size.

            // You can also read KeySize/BlockSize properties off the algorithm to find out the sizes.

            byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));



            // Now we need to turn the resulting byte array into a string.

            // A common mistake would be to use an Encoding class for that. It does not work

            // because not all byte values can be represented by characters.

            // We are going to be using Base64 encoding that is designed exactly for what we are

            // trying to do.

            return System.Text.Encoding.Unicode.GetString(decryptedData);



        }
        
        /// <summary>
        /// Decrypts a byte array that has been encrypted using Encrypt function
        /// </summary>
        /// <param name="cipherData">The data to decrypt</param>
        /// <param name="Password">The password that decrypts the data</param>
        /// <returns>The decrypted byte array</returns>
        public static byte[] Decrypt(byte[] cipherData, string Password)
        {

            // We need to turn the password into Key and IV.

            // We are using salt to make it harder to guess our key using a dictionary attack -

            // trying to guess a password by enumerating all possible words.

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,

                        new byte[] { 0x85, 0x7d, 0xa1, 0x6c, 0x2a, 0x4f, 0x23, 0x78, 0x12, 0xf2, 0xa3, 0xe2, 0xaa });



            // Now get the key/IV and do the Decryption using the function that accepts byte arrays.

            // Using PasswordDeriveBytes object we are first getting 32 bytes for the Key

            // (the default Rijndael key length is 256bit = 32bytes) and then 16 bytes for the IV.

            // IV should always be the block size, which is by default 16 bytes (128 bit) for Rijndael.

            // If you are using DES/TripleDES/RC2 the block size is 8 bytes and so should be the IV size.

            // You can also read KeySize/BlockSize properties off the algorithm to find out the sizes.

            return Decrypt(cipherData, pdb.GetBytes(32), pdb.GetBytes(16));



        }
        /// <summary>
        /// Decrypts a file that has been encrypted using Encrypt
        /// </summary>
        /// <param name="fileIn">The file to decrypt</param>
        /// <param name="fileOut">Where to save the decrypted file</param>
        /// <param name="Password">The password to decrypt the file with.</param>
        public static void Decrypt(string fileIn, string fileOut, string Password)
        {

            // First we are going to open the file streams

            FileStream fsIn = new FileStream(fileIn, FileMode.Open, FileAccess.Read);

            FileStream fsOut = new FileStream(fileOut, FileMode.OpenOrCreate, FileAccess.Write);



            // Then we are going to derive a Key and an IV from the Password and create an algorithm

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,

                        new byte[] { 0x85, 0x7d, 0xa1, 0x6c, 0x2a, 0x4f, 0x23, 0x78, 0x12, 0xf2, 0xa3, 0xe2, 0xaa });



            Rijndael alg = Rijndael.Create();



            alg.Key = pdb.GetBytes(32);

            alg.IV = pdb.GetBytes(16);



            // Now create a crypto stream through which we are going to be pumping data.

            // Our fileOut is going to be receiving the Decrypted bytes.

            CryptoStream cs = new CryptoStream(fsOut, alg.CreateDecryptor(), CryptoStreamMode.Write);



            // Now will will initialize a buffer and will be processing the input file in chunks.

            // This is done to avoid reading the whole file (which can be huge) into memory.

            int bufferLen = 4096;

            byte[] buffer = new byte[bufferLen];

            int bytesRead;



            do
            {

                // read a chunk of data from the input file

                bytesRead = fsIn.Read(buffer, 0, bufferLen);



                // Decrypt it

                cs.Write(buffer, 0, bytesRead);



            } while (bytesRead != 0);



            // close everything

            cs.Close(); // this will also close the unrelying fsOut stream

            fsIn.Close();

        }

    }

}